package com.travelation.persistence;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

public class MongoLabClient {

	private String apiKey = "4f7b8b06e4b0d23ab92f6ca7";
	private String database;
	private String currentCollection;
	private static final String PROTOCOL = "https";
	private static final String SERVER = "mongolab.com";
	private static final int PORT = 443;
	private String encoding = "UTF-8";
	private HttpClient client = new DefaultHttpClient();

	public MongoLabClient(String database, String collection) {
		this.database = database;
		this.currentCollection = collection;
	}

	public void setCurrentCollection(String collection) {
		this.currentCollection = collection;
	}

	public void setEncoding(String encoding) {
		this.encoding = encoding;
	}

	private String getApiUrl() {
		return "api/1/databases/" + database + "/collections/"
				+ currentCollection;
	}

	private URI createURI(List<NameValuePair> qparams, String apiUrl)
			throws URISyntaxException {
		return URIUtils.createURI(PROTOCOL, SERVER, PORT, apiUrl,
				URLEncodedUtils.format(qparams, encoding), null);
	}

	public HttpResponse insert(JSONObject object) {

		try {
			String query = getApiUrl();
			List<NameValuePair> qparams = new ArrayList<NameValuePair>();
			qparams.add(new BasicNameValuePair("apiKey", apiKey));

			URI uri = createURI(qparams, query);
			//
			System.out.println("URI:" + uri.toString());

			HttpPost post = new HttpPost(uri);
			post.setHeader("Content-type", "application/json");
			System.out.println("INFO:" + object.toString());
			post.setEntity(new StringEntity(object.toString()));
			HttpResponse response = client.execute(post);
			System.out.println(response.getStatusLine().getStatusCode());
			return response;

		} catch (URISyntaxException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (IllegalStateException e) {
			e.printStackTrace();
		}
		return null;
	}

	public HttpResponse update(JSONObject id, JSONObject json) {
		try {

			String query = getApiUrl();

			List<NameValuePair> qparams = new ArrayList<NameValuePair>();
			qparams.add(new BasicNameValuePair("apiKey", apiKey));
			qparams.add(new BasicNameValuePair("q", id.toString()));

			URI uri = createURI(qparams, query);

			HttpPut put = new HttpPut(uri);
			StringEntity se = new StringEntity(json.toString());
			put.setEntity(se);
			put.setHeader("Content-type", "application/json");

			HttpResponse response = client.execute(put);
			return response;
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		return null;
	}

	public JSONObject get(JSONObject id) {

		try {
			List<NameValuePair> qp = new ArrayList<NameValuePair>();
			qp.add(new BasicNameValuePair("q", id.toString()));
			JSONArray array = this.list(qp);
			if (array.length() == 0)
				return null;
			String search = getApiUrl()
					+ "/"
					+ array.getJSONObject(0).getJSONObject("_id")
							.getString("$oid");

			List<NameValuePair> qparams = new ArrayList<NameValuePair>();
			qparams.add(new BasicNameValuePair("apiKey", apiKey));

			URI uri = createURI(qparams, search);

			HttpGet req = new HttpGet(uri);
			HttpResponse response = client.execute(req);
			String json = getResponse(response);

			return new JSONObject(json);
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JSONException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		return null;

	}

	public JSONArray list(List<NameValuePair> qparams) {
		/*
		 * example query: qparams.add(new BasicNameValuePair("q",
		 * "{ \"active\":\"yes\" }")); returns all documents with an "active"
		 * field with value "yes"
		 */

		try {
			String search = getApiUrl();

			qparams.add(new BasicNameValuePair("apiKey", apiKey));

			URI uri = createURI(qparams, search);

			HttpGet req = new HttpGet(uri);
			HttpResponse response = client.execute(req);
			String json = getResponse(response);

			return new JSONArray(json);
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JSONException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		return null;
	}

	public JSONArray findNear(double x, double y, double maxDistance) {

		try {

			String query = "api/1/databases/" + database + "/runCommand";
			List<NameValuePair> qparams = new ArrayList<NameValuePair>();
			qparams.add(new BasicNameValuePair("apiKey", apiKey));
			URI uri = createURI(qparams, query);
			System.out.println(uri);
			HttpPost post = new HttpPost(uri);
			post.setHeader("Content-type", "application/json");

			String jsonCommand = "{\"geoNear\": \"" + currentCollection
					+ "\",\"near\": [" + x + "," + y + "],\"maxDistance\": "
					+ maxDistance + "}";

			post.setEntity(new StringEntity(jsonCommand));
			HttpResponse response = client.execute(post);

			JSONObject object = new JSONObject(getResponse(response));
			JSONArray array = (JSONArray) object.get("results");
			JSONArray ret = new JSONArray();

			for (int i = 0; i < array.length(); i++) {
				ret.put(array.getJSONObject(i).get("obj"));
			}
			return ret;

		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (JSONException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		return null;
	}

	public JSONObject findOne(List<NameValuePair> qparams) {
		try {
			String search = getApiUrl();

			qparams.add(new BasicNameValuePair("apiKey", apiKey));
			qparams.add(new BasicNameValuePair("fo", "true"));

			URI uri = createURI(qparams, search);

			HttpGet req = new HttpGet(uri);
			HttpResponse response = client.execute(req);
			String json = getResponse(response);
			return new JSONObject(json);
		} catch (Exception e) {
			return null;
		}
	}

	public HttpResponse delete(JSONObject id) throws JSONException {
		try {

			List<NameValuePair> qp = new ArrayList<NameValuePair>();
			qp.add(new BasicNameValuePair("q", id.toString()));
			JSONArray array = this.list(qp);
			if (array.length() == 0)
				return null;
			String query = getApiUrl()
					+ "/"
					+ array.getJSONObject(0).getJSONObject("_id")
							.getString("$oid");

			List<NameValuePair> qparams = new ArrayList<NameValuePair>();
			qparams.add(new BasicNameValuePair("apiKey", apiKey));
			URI uri = createURI(qparams, query);
			System.out.println(uri.toString());
			HttpDelete del = new HttpDelete(uri);
			HttpResponse response = client.execute(del);
			return response;
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
		return null;
	}

	private String getResponse(HttpResponse response)
			throws IllegalStateException, IOException {

		HttpEntity entity = response.getEntity();
		InputStream in = entity.getContent();
		String json = "";
		String line = "";
		BufferedReader reader = new BufferedReader(new InputStreamReader(in));
		while ((line = reader.readLine()) != null) {
			json += line;
		}
		return json;

	}

}